package com.rikylee.bootreverseproxy.service;

import com.alibaba.fastjson2.JSONObject;
import com.rikylee.bootreverseproxy.constant.MyConstants;
import com.rikylee.bootreverseproxy.entity.RouteInstance;
import com.rikylee.bootreverseproxy.entity.ServerInstance;
import com.rikylee.bootreverseproxy.utils.HttpRequestMapper;
import com.rikylee.bootreverseproxy.utils.HttpResponseMapper;
import jakarta.annotation.Resource;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.http.RequestEntity;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;

import java.io.IOException;

@Slf4j
@Service
public class RoutingDelegateService {
  
  private HttpResponseMapper responseMapper;
  
  private HttpRequestMapper requestMapper;
  
  @Resource
  private RestTemplate restTemplate;
  
  /**
   * 根据相应策略转发请求到对应后端服务
   *
   * @param ab RouteInstance
   * @param request  HttpServletRequest
   * @param response HttpServletResponse
   */
  public void doForward(RouteInstance ab, HttpServletRequest request, HttpServletResponse response) {
    RouteInstance tmpRoute = new RouteInstance();
  
    tmpRoute.setUri(ab.getUri());
    tmpRoute.setPath(ab.getPath());
    tmpRoute.setRewrite(ab.isRewrite());
    
    boolean shouldLB = StringUtils.startsWith(tmpRoute.getUri(), MyConstants.LB_PREFIX);
    if (shouldLB) {
      // 需要负载均衡,获取appName
      String appName = StringUtils.substringAfter(tmpRoute.getUri(), MyConstants.LB_PREFIX);
      //从请求头中获取是否必须按user去路由到同一节点
      // 可用节点
      ServerInstance chooseInstance = chooseLBInstance(appName);
      if (chooseInstance == null) {
        // 无可用节点，返回异常,
        JSONObject result = new JSONObject();
        result.put("status", MyConstants.NO_AVAILABLE_NODE_STATUS);
        result.put("msg", MyConstants.NO_AVAILABLE_NODE_MSG);
        renderString(response, result.toJSONString());
        return;
      } else {
        //设置route instance uri 为负载均衡之后的URI地址
        String uri = MyConstants.HTTP_PREFIX + chooseInstance.getHost() + ":" + chooseInstance.getPort();
        tmpRoute.setUri(uri);
      }
    }
    // 转发请求
    try {
      goForward(request, response, tmpRoute);
    } catch (Exception e) {
      // 连接超时、返回异常
      e.printStackTrace();
      log.error("request error {}", e.getMessage());
      JSONObject result = new JSONObject();
      result.put("status", MyConstants.UNKNOWN_EXCEPTION_STATUS);
      result.put("msg", e.getMessage());
      renderString(response, result.toJSONString());
    }
    
  }
  
  /**
   * 发送请求到对应后端服务
   *
   * @param request  HttpServletRequest
   * @param response HttpServletResponse
   * @param instance RouteInstance
   * @throws IOException
   */
  private void goForward(HttpServletRequest request, HttpServletResponse response, RouteInstance instance) throws IOException {
    requestMapper = new HttpRequestMapper();
    RequestEntity<byte[]> requestEntity = requestMapper.map(request, instance);
    //用byte数组处理返回结果，因为返回结果可能是字符串也可能是数据流
    ResponseEntity<byte[]> responseEntity = restTemplate.exchange(requestEntity, byte[].class);
    responseMapper = new HttpResponseMapper();
    responseMapper.map(responseEntity, response);
  }
  
  private ServerInstance chooseLBInstance(String appName) {
    //TODO 根据appName 选择对应的host
    ServerInstance instance = new ServerInstance();
    instance.setHost("127.0.0.1");
    instance.setPort(10000);
    return instance;
  }
  
  /**
   * 写回字符串结果到客户端
   *
   * @param response
   * @param string
   */
  public void renderString(HttpServletResponse response, String string) {
    try {
      response.setStatus(200);
      response.setContentType("application/json");
      response.setCharacterEncoding("utf-8");
      response.getWriter().print(string);
    } catch (IOException e) {
      e.printStackTrace();
    }
  }
}
